<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>元素自动避障演示</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link
      href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css"
      rel="stylesheet" />
    <script>
      tailwind.config = {
        theme: {
          extend: {
            colors: {
              primary: "#3B82F6",
              secondary: "#10B981",
              accent: "#F59E0B",
              danger: "#EF4444",
              neutral: "#1F2937",
              "neutral-light": "#F3F4F6",
            },
            fontFamily: {
              sans: ["Inter", "system-ui", "sans-serif"],
            },
          },
        },
      };
    </script>
    <style type="text/tailwindcss">
      @layer utilities {
        .content-auto {
          content-visibility: auto;
        }
        .floating-element {
          position: absolute;
          border-radius: 0.5rem;
          padding: 0.5rem;
          display: flex;
          align-items: center;
          justify-content: center;
          transition: all 0.3s ease;
          cursor: move;
          user-select: none;
          box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
            0 2px 4px -1px rgba(0, 0, 0, 0.06);
        }
        .floating-element:hover {
          transform: translateY(-2px);
          box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
            0 4px 6px -2px rgba(0, 0, 0, 0.05);
        }
        .floating-element.colliding {
          border: 2px solid #ef4444;
          background-color: rgba(239, 68, 68, 0.1);
        }
        .control-panel {
          position: fixed;
          bottom: 2rem;
          left: 50%;
          transform: translateX(-50%);
          background-color: white;
          padding: 1rem;
          border-radius: 0.75rem;
          box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
          z-index: 100;
        }
        .btn-action {
          @apply bg-primary text-white px-4 py-2 rounded-lg shadow-md hover:bg-primary/90 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-primary/50 active:scale-95;
        }
        .btn-toggle {
          @apply bg-secondary text-white px-4 py-2 rounded-lg shadow-md hover:bg-secondary/90 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-secondary/50 active:scale-95;
        }
        .btn-reset {
          @apply bg-neutral text-white px-4 py-2 rounded-lg shadow-md hover:bg-neutral/90 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-neutral/50 active:scale-95;
        }
      }
    </style>
  </head>
  <body class="bg-gray-50 font-sans overflow-hidden">
    <header class="bg-white shadow-sm">
      <div
        class="container mx-auto px-4 py-4 flex justify-between items-center">
        <h1 class="text-xl font-bold text-neutral">元素自动避障演示</h1>
        <div class="flex items-center space-x-2">
          <span
            id="collisionCount"
            class="px-3 py-1 bg-danger/20 text-danger rounded-full font-medium text-sm">
            碰撞次数: 0
          </span>
          <span
            id="adjustmentCount"
            class="px-3 py-1 bg-secondary/20 text-secondary rounded-full font-medium text-sm">
            调整次数: 0
          </span>
        </div>
      </div>
    </header>

    <main
      id="viewport"
      class="relative w-full h-[calc(100vh-4rem)] bg-gray-100 overflow-hidden">
      <!-- 元素将通过JavaScript动态生成 -->
    </main>

    <div class="control-panel flex flex-wrap gap-2 justify-center">
      <button id="addElement" class="btn-action">
        <i class="fa fa-plus mr-2"></i>添加元素
      </button>
      <button id="startAvoidance" class="btn-toggle">
        <i class="fa fa-play mr-2"></i>开始避障
      </button>
      <button id="reset" class="btn-reset">
        <i class="fa fa-refresh mr-2"></i>重置
      </button>
      <div class="flex items-center gap-2 px-2">
        <label for="avoidanceStrength" class="text-sm text-gray-600"
          >避障强度:</label
        >
        <input
          type="range"
          id="avoidanceStrength"
          min="1"
          max="20"
          value="10"
          class="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" />
        <span id="strengthValue" class="text-sm font-medium">10</span>
      </div>
    </div>

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const viewport = document.getElementById("viewport");
        const addElementBtn = document.getElementById("addElement");
        const startAvoidanceBtn = document.getElementById("startAvoidance");
        const resetBtn = document.getElementById("reset");
        const avoidanceStrengthInput =
          document.getElementById("avoidanceStrength");
        const strengthValueDisplay = document.getElementById("strengthValue");
        const collisionCountDisplay = document.getElementById("collisionCount");
        const adjustmentCountDisplay =
          document.getElementById("adjustmentCount");

        let elements = [];
        let isAvoiding = false;
        let avoidanceStrength = parseInt(avoidanceStrengthInput.value);
        let collisionCount = 0;
        let adjustmentCount = 0;

        // 随机颜色数组
        const colors = [
          "bg-primary/80",
          "bg-secondary/80",
          "bg-accent/80",
          "bg-purple-500/80",
          "bg-pink-500/80",
          "bg-indigo-500/80",
          "bg-teal-500/80",
          "bg-orange-500/80",
          "bg-emerald-500/80",
        ];

        // 生成随机位置和大小的元素
        function createRandomElement() {
          const viewportRect = viewport.getBoundingClientRect();
          const width = Math.floor(Math.random() * 100) + 100; // 100-200px
          const height = Math.floor(Math.random() * 60) + 40; // 40-100px
          const maxX = viewportRect.width - width;
          const maxY = viewportRect.height - height;

          const element = document.createElement("div");
          element.className = `floating-element ${
            colors[Math.floor(Math.random() * colors.length)]
          }`;
          element.style.width = `${width}px`;
          element.style.height = `${height}px`;
          element.style.left = `${Math.floor(Math.random() * maxX)}px`;
          element.style.top = `${Math.floor(Math.random() * maxY)}px`;

          // 为元素添加唯一标识和内容
          const id = Date.now() + Math.floor(Math.random() * 1000);
          element.id = `element-${id}`;
          element.innerHTML = `
                    <div class="flex flex-col items-center">
                        <i class="fa fa-cube text-2xl mb-1"></i>
                        <span class="font-medium">元素 ${
                          elements.length + 1
                        }</span>
                    </div>
                `;

          // 添加拖拽功能
          makeDraggable(element);

          viewport.appendChild(element);
          elements.push(element);

          // 立即检查碰撞
          checkCollisions();
        }

        // 使元素可拖拽
        function makeDraggable(element) {
          let isDragging = false;
          let offsetX, offsetY;

          element.addEventListener("mousedown", function (e) {
            isDragging = true;
            const rect = element.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            element.style.zIndex = 1000 + elements.length; // 拖拽时置于顶层
            element.classList.add("scale-105");
          });

          document.addEventListener("mousemove", function (e) {
            if (!isDragging) return;

            const viewportRect = viewport.getBoundingClientRect();
            const rect = element.getBoundingClientRect();

            // 计算新位置，确保不超出视口
            let newX = e.clientX - offsetX - viewportRect.left;
            let newY = e.clientY - offsetY - viewportRect.top;

            // 边界检查
            newX = Math.max(0, Math.min(newX, viewportRect.width - rect.width));
            newY = Math.max(
              0,
              Math.min(newY, viewportRect.height - rect.height)
            );

            element.style.left = `${newX}px`;
            element.style.top = `${newY}px`;

            // 拖拽时检查碰撞
            checkCollisions();
          });

          document.addEventListener("mouseup", function () {
            isDragging = false;
            element.classList.remove("scale-105");
          });
        }

        // 检测两个元素是否碰撞
        function checkCollision(element1, element2) {
          const rect1 = element1.getBoundingClientRect();
          const rect2 = element2.getBoundingClientRect();

          return (
            rect1.left < rect2.right &&
            rect1.right > rect2.left &&
            rect1.top < rect2.bottom &&
            rect1.bottom > rect2.top
          );
        }

        // 检查所有元素之间的碰撞
        function checkCollisions() {
          let hasCollision = false;

          // 重置所有元素的碰撞状态
          elements.forEach(el => el.classList.remove("colliding"));

          for (let i = 0; i < elements.length; i++) {
            for (let j = i + 1; j < elements.length; j++) {
              if (checkCollision(elements[i], elements[j])) {
                elements[i].classList.add("colliding");
                elements[j].classList.add("colliding");
                hasCollision = true;
                collisionCount++;
                updateCounters();

                // 如果启用了避障，执行避障逻辑
                if (isAvoiding) {
                  avoidCollision(elements[i], elements[j]);
                }
              }
            }
          }

          return hasCollision;
        }

        // 避障算法：基于斥力模型
        function avoidCollision(element1, element2) {
          const rect1 = element1.getBoundingClientRect();
          const rect2 = element2.getBoundingClientRect();

          // 计算两个元素中心之间的向量
          const center1 = {
            x: rect1.left + rect1.width / 2,
            y: rect1.top + rect1.height / 2,
          };

          const center2 = {
            x: rect2.left + rect2.width / 2,
            y: rect2.top + rect2.height / 2,
          };

          // 计算向量差
          let dx = center2.x - center1.x;
          let dy = center2.y - center1.y;

          // 计算距离（添加小值避免除零错误）
          const distance = Math.sqrt(dx * dx + dy * dy) + 0.001;

          // 计算斥力强度（距离越近，斥力越大）
          // const repulsionForce = avoidanceStrength * (1 / distance);
          const repulsionForce = 10000 * (1 / distance);

          // 归一化向量
          const normalX = dx / distance;
          const normalY = dy / distance;

          // 计算移动量
          const moveAmount = {
            x: normalX * repulsionForce,
            y: normalY * repulsionForce,
          };

          // 移动元素
          moveElementWithConstraints(element1, -moveAmount.x, -moveAmount.y);
          moveElementWithConstraints(element2, moveAmount.x, moveAmount.y);

          adjustmentCount++;
          updateCounters();
        }

        // 移动元素并确保不超出视口
        function moveElementWithConstraints(element, dx, dy) {
          const viewportRect = viewport.getBoundingClientRect();
          const rect = element.getBoundingClientRect();

          // 获取当前位置
          let currentX = rect.left - viewportRect.left;
          let currentY = rect.top - viewportRect.top;

          // 计算新位置
          let newX = currentX + dx;
          let newY = currentY + dy;

          // 边界检查
          if (newX < 0) newX = 0;
          if (newY < 0) newY = 0;
          if (newX + rect.width > viewportRect.width) {
            newX = viewportRect.width - rect.width;
          }
          if (newY + rect.height > viewportRect.height) {
            newY = viewportRect.height - rect.height;
          }

          // 应用新位置
          element.style.left = `${newX}px`;
          element.style.top = `${newY}px`;
        }

        // 更新计数器显示
        function updateCounters() {
          collisionCountDisplay.textContent = `碰撞次数: ${collisionCount}`;
          adjustmentCountDisplay.textContent = `调整次数: ${adjustmentCount}`;
        }

        // 避障循环
        function startAvoidanceLoop() {
          if (!isAvoiding) return;

          // 检查并处理碰撞
          checkCollisions();

          // 继续下一帧
          requestAnimationFrame(startAvoidanceLoop);
        }

        // 事件监听
        addElementBtn.addEventListener("click", createRandomElement);

        startAvoidanceBtn.addEventListener("click", function () {
          isAvoiding = !isAvoiding;

          if (isAvoiding) {
            startAvoidanceBtn.innerHTML =
              '<i class="fa fa-pause mr-2"></i>暂停避障';
            startAvoidanceBtn.classList.remove("bg-secondary");
            startAvoidanceBtn.classList.add("bg-amber-500");
            startAvoidanceLoop();
          } else {
            startAvoidanceBtn.innerHTML =
              '<i class="fa fa-play mr-2"></i>开始避障';
            startAvoidanceBtn.classList.remove("bg-amber-500");
            startAvoidanceBtn.classList.add("bg-secondary");
          }
        });

        resetBtn.addEventListener("click", function () {
          // 移除所有元素
          elements.forEach(el => el.remove());
          elements = [];

          // 重置状态
          isAvoiding = false;
          startAvoidanceBtn.innerHTML =
            '<i class="fa fa-play mr-2"></i>开始避障';
          startAvoidanceBtn.classList.remove("bg-amber-500");
          startAvoidanceBtn.classList.add("bg-secondary");

          // 重置计数器
          collisionCount = 0;
          adjustmentCount = 0;
          updateCounters();

          // 创建初始元素
          createRandomElement();
          createRandomElement();
          createRandomElement();
        });

        avoidanceStrengthInput.addEventListener("input", function () {
          avoidanceStrength = parseInt(this.value);
          strengthValueDisplay.textContent = avoidanceStrength;
        });

        // 初始化：创建3个随机元素
        createRandomElement();
        createRandomElement();
        createRandomElement();
      });
    </script>
  </body>
</html>
